iT邦幫忙

2024 iThome 鐵人賽

DAY 0
0
自我挑戰組

30天的自我成長紀錄系列 第 9

[2024鐵人賽]第9天:物件導向程式設計-多重繼承&虛擬基礎類別

  • 分享至 

  • xImage
  •  

💡本筆記內容源自溫宏斌老師的物件導向程式設計OCW。

🖊️ 章節重點

多重繼承

虛擬基礎類別

🖊️ 多重繼承(multiple inheritance)

(一)概念
1.衍生類別繼承多個父類別,作法一個個列出來被繼承的類別(父類別)。

2.回顧繼承特性(假設class Boo 公開繼承Bum和Foo)
(1)支援Bum和Foo公開/受保護的方法
(2)由於是衍生類別,Boo的物件可以轉換成Bum或Foo的物件(如前一章圓vs圓柱體)。
(3)當你想創造新的Boo物件,你也會呼叫Bum或Foo預設的constructors,並用反著呼叫destructors。

💡注意: 呼叫建構子的順序是按照宣告順序從左到右,而非初始化的順序(見本章第三部分)。

3.多重繼承以圖示來看如下圖
https://ithelp.ithome.com.tw/upload/images/20241013/20169520uCTVqYOQx6.png]

4.缺點:
多重繼承較單繼承來得容易混淆,但如果用到多重繼承,且兩個父類別有相同的member,就要使用界定符號resolution operator(::)。

class Sofa{
public: void sit(){
    cout << "sit" <<endl;}
    void SetWeight(int a=0){weight=a;}
    ....
};

class Bed{
public: void lie(){
    cout << "lie" <<endl;}
    void SetWeight(int a=0){weight=a;}
    ....
};
//公開繼承
class Sofabed:public Sofa, public Bed{{
public: void fold(){
    cout << "fold" <<endl;}

//
int main()
{
    Sofabed myfur;
    myfur.sit();
    myfur.lie();
    
    return 0;
}

(二)多重繼承產生的模糊地帶
1.發生情境
(1)從父類別呼叫了相同名字的member
(2)呼叫到共同基礎類別的member,也就是說,父類別也是繼承某類別來的(p.9)

2.重載同名成員以後出現的副作用:
(1)重載基礎類別底下同名的member後,會使他們不能直接被存取(access)。

//基礎類別
class CB{
public:
    void f(){cout << "CB's f()"<<endl;}
    void f(int x){cout << "CB's f()"<<endl;}
}
//衍生類別:公開繼承
class CD: public CB{
public:
    void f(){cout << "CD's f()"<<endl;}
}
//main()
CD obj;
obj.f();
obj.f(5); //錯誤,會顯示不該有參數

例如上面的程式碼因衍生類別CD裡面有void f()蓋掉了基礎類別中void f()和void f(int x),即便參數數量不同,但因函式名稱相同還是被蓋掉,所以下半部obj.f(5)會error,除非用obj.CB::f(5)。

(2)父類別也是繼承其他類別而來,除了產生歧異性或混淆不清的情況,還會浪費記憶體。
https://ithelp.ithome.com.tw/upload/images/20241013/20169520orvNRnW7ZS.png
如上圖,CB繼承CA、CC繼承CA,CD包含了兩個copy的CA。
用程式碼來看,可以發現錯誤在於:

//衍生類別:公開繼承
class CD: public CB, public CC{
public:
    int w;
    CD(int a = 0,int b = 0,int c = 0,int d = 0,int e = 0):CB(a,b),CC(c,d){w=e;}
    ....
}
//main()
CD obj(5,4,3,2,1);//錯誤,因為CB和CC中的x應該是同一個,但因硬寫成兩次,導致兩個x copy最終出現的值不同。

3.解方
虛擬基礎類別(如下)

🖊️ 虛擬基礎類別

(一)概念
虛基類的用途在於當定義一個衍生類別,且這個衍生類別是繼承到同一個基礎類別時,只保留一份的copy,如前例CB和CC都有繼承到CA的x,此時只會留下一份x。

//CA
class CA{
public:
    int x;
    CA(int a=0){x=a;}
};
//CB:虛擬繼承CA
class CB:**virtual** public CA{
public:
    int y;
    CB(int a=0, int b=0):CA(a){y=b;}
};
//CC:虛擬繼承CA
class CB:**virtual** public CA{
public:
    int z;
    CC(int a=0, int b=0):CA(a){z=b;}
};

//CD:
class CD: public CB, public CC{
public:
    int w;
    //在呼叫CB,CC以前,用CA的建構子初始化x(a)欄位,x的結果會是一致的。
    CD(int a = 0,int b = 0,int c = 0,int d = 0,int e = 0):**CA(a)**,CB(a,b),CC(c,d){w=e;}
    ....
}

⭐⭐虛基類的constructor要寫在最前面,不能沒寫CA(a),否則他就會回到預設初始值

(二)重構前面沙發的例子
1.redesign SofaBed在Sofa和Bed之上再定一層
https://ithelp.ithome.com.tw/upload/images/20241013/20169520SjpRBrgu8M.png

//作為共同的base class
class Furniture{
protected: int weight;
public:
    void SetWeight(int a=0){weight=a;}
    ...
};

//Sofa
class Sofa: virtual public Furniture {
public: void sit(){
    cout << "sit" <<endl;}
};

class Bed: virtual public Furniture {
public: void lie(){
    cout << "lie" <<endl;}
};

//Sofabed
class Sofabed:public Sofa, public Bed{
public: void fold(){
    cout << "fold" <<endl;}
};

//
int main()
{
    Sofabed obj;
    obj.SetWeight(100);//因為使用虛基類,所以是Furniture的SetWeight,把她餵進Furniture的void SetWeight。
    
    return 0;
}
//Bed

🖊️ 建構子和解構子的執行順序⭐

1.從宣告的順序,而非始化順序找虛基類是誰,先做虛基類的初始化,做完以後才找其他類別的初始化,以下圖為例,由於宣告順序是CB再來CC,所以會從CB找到虛基類CA,初始化CA->CB->CC。

https://ithelp.ithome.com.tw/upload/images/20241013/20169520S9n6molYLl.png

2.解構子是從相反順序

https://ithelp.ithome.com.tw/upload/images/20241013/20169520ChepSzCM70.png
https://ithelp.ithome.com.tw/upload/images/20241013/20169520u63Qihckka.png

🤔 疑問

目前無

📖 下一章節主題預告

多型


上一篇
[2024鐵人賽]第8天:物件導向程式設計-繼承的基本概念&單繼承
下一篇
[2024鐵人賽]第10天:物件導向程式設計-多型
系列文
30天的自我成長紀錄14
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言